/*
 * VoltageDividerCircuit.hpp
 *
 * Copyright 2012 Pieter Agten
 *
 * This file is part of tltgen.
 *
 * tltgen is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * tltgen is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with tltgen.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * @file   VoltageDividerCircuit.hpp
 * @author Pieter Agten (pieter.agten@gmail.com)
 * @date   18 mar 2012
 */

#ifndef VOLTAGEDIVIDERCIRCUIT_HPP
#define VOLTAGEDIVIDERCIRCUIT_HPP

#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/math/special_functions/nonfinite_num_facets.hpp>

#include "Circuit.hpp"
#include "Resistance.hpp"
#include "Temperature.hpp"
#include "Voltage.hpp"
#include "AdcValue.hpp"

#include "SerializationException.hpp"

namespace tltgen {

  class CircuitVisitor;

  /**
   * The VoltageDivider class models the following circuit:
   * 
   *    Vref______
   *            _|_
   *           |   |
   *           | R |
   *           |div|
   *           |___|
   *             |______________V (to ADC)
   *            _|_      _|_  
   *           |the|    |   |
   *           |rmi|    | R |
   *           |sto|    |par|
   *           |_r_|    |_ _|
   *   GND_______|________|
   *
   */
  class VoltageDividerCircuit : public Circuit {
  public:
    /**
     * Constructs a new VoltageDividerCircuit instance with a given resistor 2 value.
     *
     * @param vin the input voltage to use in the new circuit
     * @param rdiv the resistance to use for the divider resistor in the new circuit
     */
    VoltageDividerCircuit(Voltage vin, Resistance rdiv, Resistance rpar = tltgen::openCircuitResistance());
    virtual ~VoltageDividerCircuit();

    /**
     * Returns whether a given voltage is a valid input voltage to be used 
     * for constructing a new VoltageDividerCircuit instance.
     *
     * @param  vin the voltage to check
     * @return whether \a ref is a valid input voltage to be used for
     *         constructing a new VoltageDividerCircuit instance.
     */
    static bool isValidInputVoltage(Voltage vinput);

    /**
     * Returns whether a given resistance is a valid resistance to be used for
     * the divider resistor in a new VoltageDividerCircuit instance.
     *
     * @param  rdiv the resistance to check
     * @return whether \a rdiv is a valid resistance to be used for resistor 2 in
     *         a new VoltageDividerCircuit instance.
     */
    static bool isValidDividerResistance(Resistance rdiv);


    static bool isValidParallelResistance(Resistance rpar);

    /**
     * Returns the input voltage used in this circuit.
     * 
     * @return the input voltage used in this circuit
     */
    virtual Voltage getInputVoltage() const;

    /**
     * Returns the resistance of the divider resistor of this circuit.
     * 
     * @return the resistance of the divider resistor of this circuit
     */
    virtual Resistance getDividerResistance() const;

    virtual bool hasParallelResistor() const;

    virtual Resistance getParallelResistance() const;

    /** 
     * @copydoc Circuit::isValidOutputVoltage(Voltage)
     */
    virtual bool isValidOutputVoltage(Voltage voltage) const;

    /** 
     * @copydoc Circuit::getThermistorResistance()
     */    
    virtual Resistance getThermistorResistance(Voltage outputVoltage) const;


    /** 
     * @copydoc Circuit::isValidThermistorResistance(Resistance)
     */
    virtual bool isValidThermistorResistance(Resistance resistance) const;

    /** 
     * @copydoc Circuit::getOutputVoltage()
     */
    virtual Voltage getOutputVoltage(Resistance thermistorResistance) const;

    virtual Power getThermistorPowerDissipationAtResistance(Resistance Rt) const;

    virtual Power getThermistorPowerDissipationAtOutputVoltage(Voltage Vout) const;

    virtual void accept(CircuitVisitor* visitor) const;

  private:
    const Voltage vin;
    const Resistance rdiv;
    const Resistance rpar;

    friend class boost::serialization::access;
    template<class Archive> void serialize(Archive & ar, unsigned int version)
    {
      if (version != 0) {
        throw tltgen::SerializationException("Unknown version " + 
					     boost::lexical_cast<std::string>(version) + " for VoltageDividerCircuit.");
      }

      ar & boost::serialization::base_object<Circuit>(*this);
    }
  };

}

BOOST_CLASS_VERSION(tltgen::VoltageDividerCircuit, 0)
BOOST_CLASS_EXPORT_KEY(tltgen::VoltageDividerCircuit)

namespace boost {
  namespace serialization {
    template<class Archive>
    inline void save_construct_data(Archive &ar, const tltgen::VoltageDividerCircuit *s, const unsigned int version)
    {
      if (version != 0) {
        throw tltgen::SerializationException("Unknown version " + 
					     lexical_cast<std::string>(version) + " for saving construct data for VoltageDividerCircuit.");
      }

      tltgen::Voltage vin = s->getInputVoltage();
      tltgen::Resistance rdiv = s->getDividerResistance();
      tltgen::Resistance rpar = s->getParallelResistance();
      ar << vin;
      ar << rdiv;
      ar << rpar;
    }

    template<class Archive>
    inline void load_construct_data(Archive &ar, tltgen::VoltageDividerCircuit *s, const unsigned int version)
    {
      if (version != 0) {
        throw tltgen::SerializationException("Unknown version " + 
					     lexical_cast<std::string>(version) + " for loading construct data for VoltageDividerCircuit.");
      }
      
      tltgen::Voltage vin;
      tltgen::Resistance rdiv;
      tltgen::Resistance rpar;
      ar >> vin;
      ar >> rdiv;
      ar >> rpar;
      ::new(s) tltgen::VoltageDividerCircuit(vin, rdiv, rpar);
    }
  }
}

#endif
